
//
// 110523 - AH - (Garage) Created.
// 110526 - AH - Finished implementation.
// 110527 - AH - (Garage) Fires ChangeEvent.
// 110529 - AH - (Victoria) getLeftSelected() and getRightSelected().
// 110608 - AH - Implemented ListCellRenderer so shows SoekratName string
//               presentations in left and right combo boxes.
// 110705 - AH - (Opera) Listen SoekratChangeEvent so can update.
// 110818 - AH - (Ares) Fixed problems with updating the combo boxes
//               that contains nodes.
// 110819 - AH - (Ares *) Fixed that selecting X in middle combo box
//               only removed from to link.
// 120129 - AH - (Midtun) Uses new ToolTipComboBox.
//

package org.soekrat.gui.swing.network;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.soekrat.engine.event.SoekratChangeEvent;
import org.soekrat.engine.event.SoekratChangeListener;
import org.soekrat.engine.network.Network;
import org.soekrat.engine.network.NetworkNode;
import org.soekrat.gui.swing.TestFrame;
import org.soekrat.gui.swing.tools.ToolTipComboBox;

/**
 * <p>
 *   Editor for 
 *   {@link Network} links using {@link JComboBox}es.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) 
 */
@SuppressWarnings("serial")
public final class NetworkComboEditor extends JPanel 
{
  private transient List<ChangeListener> m_listeners=null;
  
  /**
   * <p>
   *   Objects shown in middle combo box.
   * </p>
   * @author Arne Halvorsen (AH)
   */
  private enum LinkType 
  { 
    NON, 
    UNI, 
    BI;
    
    public String toString() 
    {
      switch (this)
      {
        case NON : return "X";
        case UNI : return ">";
        case BI :  return "<>";
        default : throw new Error();
      }
    };
  };
  
  private JComboBox m_left=new ToolTipComboBox(); 
  
  private JComboBox m_middle=new JComboBox();
  
  private JComboBox m_right=new ToolTipComboBox();
  
  private Network m_network=null;
  
  private NetworkNode m_leftSelected=null;
  
  private NetworkNode m_rightSelected=null;
  
  private boolean m_userAction=true;
  
  private final static ListCellRenderer c_renederer=new ListCellRenderer()
  {
    private final DefaultListCellRenderer m_dlcr=new DefaultListCellRenderer();
    
    public Component getListCellRendererComponent(JList list, Object value,
      int idx, boolean selected, boolean focus)
    {
      String text=(value==null) ? "" : value.toString();
      
      if (value instanceof NetworkNode)
      {
        text=((NetworkNode)value).getSoekratName().toString();
      }
      
      return m_dlcr.getListCellRendererComponent(list, text, idx, selected, 
        focus);
    }
  };
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public NetworkComboEditor()
  { 
    super(new BorderLayout());
    
    Box center=new Box(BoxLayout.X_AXIS);
    add(center, BorderLayout.CENTER);
    
    m_middle.addItem(LinkType.NON);
    m_middle.addItem(LinkType.UNI);
    m_middle.addItem(LinkType.BI);
    
    m_left.setRenderer(c_renederer);
    m_right.setRenderer(c_renederer);
    
    center.add(m_left);
    center.add(m_middle);
    center.add(m_right);
    
    m_left.addItemListener
    (
      new ItemListener()
      {
        public void itemStateChanged(ItemEvent e) 
        {
          if (!m_userAction) return;
          
          m_leftSelected=(NetworkNode)m_left.getSelectedItem();
          setMidle();
          fire();
        }
      }
    );
    
    m_middle.addItemListener
    (
      new ItemListener()
      {
        public void itemStateChanged(ItemEvent e) 
        {
          if (!m_userAction) return;
          
          LinkType lt=(LinkType)m_middle.getSelectedItem();
          if (lt==null) return;
          switch (lt)
          {
            case NON :
              
              if (m_leftSelected!=null && m_rightSelected!=null)
              {
                m_network.unlink(m_leftSelected, m_rightSelected);
                m_network.unlink(m_rightSelected, m_leftSelected);
                
                //System.out.println("***");
                //System.out.println("has unlinked : "+m_leftSelected+" - "+m_rightSelected);
                //System.out.println("result (should be false): "+m_network.hasLink(m_leftSelected, m_rightSelected));
                //System.out.println("result (should be false): "+m_network.hasLink(m_rightSelected, m_leftSelected));
                //System.out.println("***");
                fire();
              }
              
            break;
   
            case UNI :
              
              if (m_leftSelected!=null && m_rightSelected!=null)
              {
                m_network.unlink(m_leftSelected, m_rightSelected);
                m_network.link(m_leftSelected, m_rightSelected);
                fire();
              }
              
            break;
            
            case BI :
              
              if (m_leftSelected!=null && m_rightSelected!=null)
              {
                //m_network.link(from, to), to)
                m_network.link(m_leftSelected, m_rightSelected);
                m_network.link(m_rightSelected, m_leftSelected);
                fire();
              }
              
            break;
          }
        }
      }
    );
    
    m_right.addItemListener
    (
      new ItemListener()
      {
        public void itemStateChanged(ItemEvent e) 
        {
          if (!m_userAction) return;
          
          m_rightSelected=(NetworkNode)m_right.getSelectedItem();
          setMidle();
          fire();
        }
      }
    );
    
  }
  
  private final SoekratChangeListener m_cl=new SoekratChangeListener()
  {
    public void changed(SoekratChangeEvent e){ refresh(); }
  };
  
  /**
   * <p>
   *   Sets 
   *   {@link Network} to edit links in.
   * </p>
   * @param network {@link Network} or {@code null} if to clear.
   */
  public void setNetwork(Network network)
  {
    if (m_network!=null)
    {
      m_network.removeSoekratChangeListener(m_cl);
    }
    
    m_network=network;
    
    populateNodes();
    
    if (m_network!=null)
    {
      m_network.addSoekratChangeListener(m_cl);
    }
  }
  
  /**
   * <p>
   *   Populates the combo boxes that contains nodes.
   * </p>
   */
  public void populateNodes()
  {
    m_left.removeAllItems();
    m_right.removeAllItems();
    
    if (m_network!=null)
    {
      for (NetworkNode node : m_network)
      {
        m_left.addItem(node);
        m_right.addItem(node);
      }
    }
    
    if (m_left.getItemCount()>0)
    {
      m_left.setSelectedIndex(0);
      m_right.setSelectedIndex(0);
    }
  }
  
  /**
   * <p>
   *   Refresh view.
   * </p>
   */
  private void refresh()
  { 
    if (m_leftSelected!=null) 
    {
      m_left.setSelectedItem(m_leftSelected);
    }
    if (m_rightSelected!=null) 
    {
      m_right.setSelectedItem(m_rightSelected);
    }
  }
  
  private void setMidle()
  {
    LinkType lt=LinkType.NON;
    
    NetworkNode left=m_leftSelected;
    NetworkNode right=m_rightSelected;
    
    if (left!=null && right!=null)
    {
      if (left!=right)
      {
        if (m_network.hasLink(left, right))
        {
          if (m_network.hasLink(right, left))
          {
            lt=LinkType.BI;
          }
          else
          {
            lt=LinkType.UNI;
          }
        }
      }
    }
    
    m_userAction=false;
    if (left!=null) m_left.setSelectedItem(left);
    if (right!=null) m_right.setSelectedItem(right);
    m_middle.setSelectedItem(lt);
    m_userAction=true;
    
    setMiddleEnabled();
  }
  
  private void setMiddleEnabled()
  {
    if (m_leftSelected==null || m_rightSelected==null || 
        m_leftSelected==m_rightSelected)
    {
      m_middle.setSelectedItem(null);
      m_middle.setEnabled(false);
    }
    else
    {
      m_middle.setEnabled(true);
    }
  }
  
  /**
   * <p>
   *   Gets node selected in left combo box.
   * </p>
   * @return Selected or {@code null} if none selected.
   */
  public NetworkNode getLeftSelected(){ return m_leftSelected; }
  
  /**
   * <p>
   *   Gets node selected in right combo box.
   * </p>
   * @return Selected or {@code null} if none selected.
   */
  public NetworkNode getRightSelected(){ return m_rightSelected; }
  
  /**
   * <p>
   *   Adds
   *   {@link ChangeListener}.
   * </p>
   * @param l Listener to add.
   */
  public synchronized void addChangeListener(ChangeListener l)
  {
    if (l==null)
    {
      throw new NullPointerException("l");
    }
    
    if (m_listeners==null) m_listeners=new ArrayList<ChangeListener>();
    if (!m_listeners.contains(l)) m_listeners.add(l);
  }
  
  /**
   * <p>
   *   Removes
   *   {@link ChangeListener}.
   * </p>
   * @param l Listener to remove.
   */
  public synchronized void removeChangeListener(ChangeListener l)
  {
    if (l==null)
    {
      throw new NullPointerException("l");
    }
    
    if (m_listeners==null) return;
    m_listeners.remove(l);
  }
  
  private final static ChangeListener[] E_CL_A=new ChangeListener[0];
  
  private final ChangeEvent m_event=new ChangeEvent(this);
  
  // Publishes m_event.
  private void fire()
  {
    if (m_listeners==null) return;
    ChangeListener[] l=E_CL_A;
    synchronized(this){ l=m_listeners.toArray(l); }
    for (ChangeListener curr : l) curr.stateChanged(m_event);
  }
  
  public static void main(String[] args) 
  {
    Network network=new Network();
    network.createNode("a");
    network.createNode("b");
    network.createNode("c");
    
    NetworkComboEditor nce=new NetworkComboEditor();
    nce.setNetwork(network);
    TestFrame.show(nce);
  }
  
}
